using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;

using Framework;

namespace SchemaMonitor
{
    [Serializable()]
    public partial class CChapterList : List<CChapter>
    {
        #region Constructors
        //Basic constructor
        public CChapterList() : base() {}
       
        //More efficient memory-allocation if size is known
        public CChapterList(int capacity) : base(capacity) {}
        
        //Shares the index (if its already been computed)
        public CChapterList(CChapterList list) : base(list.Count)
        {
            foreach (CChapter i in list)
                base.Add(i);
        }

        //Generic list (eg. from paging control), have to assume type
        public CChapterList(IList list) : base(list.Count)
        {
            foreach (CChapter i in list)
                base.Add(i);
        }
		#endregion

		public void ResetSortOrder()
		{
			this.ResetOrdinals();
		}
		#region Top/Bottom/Page
		public CChapterList Top(int count)
        {
            if (count >= this.Count)
                return this;
            return Page(count, 0); 
        }
        public CChapterList Bottom(int count)   
        {
            if (count > this.Count)
                count = this.Count;
            return new CChapterList(this.GetRange(this.Count - count - 1, count));
        }
        public CChapterList Page(int pageSize, int pageIndex)
        {
            return new CChapterList( CUtilities.Page(this, pageSize, pageIndex) );
        }
        #endregion

        #region BulkEditLogic
        public bool HaveSameValue(string propertyName)               { return CReflection.HaveSameValue(this, propertyName); }
        public void SetSameValue( string propertyName, object value) {        CReflection.SetSameValue( this, propertyName, value); }
        #endregion

        #region SortBy
        //Public
        public CChapterList SortBy(string propertyName) { return SortBy(propertyName, false); }
        public CChapterList SortBy(string propertyName, bool descending)
        {
            CChapterList copy = new CChapterList(this);
            if (this.Count == 0)    return copy;
            copy.Sort(new CChapterList_SortBy(propertyName, descending, this));
            return copy;
        }
        //Private 
        private class CChapterList_SortBy : CReflection.GenericSortBy, IComparer<CChapter>
        {
            public CChapterList_SortBy(string propertyName, bool descending, IList list) : base(propertyName, descending, list) { }
            public int Compare(CChapter x, CChapter y) { return base.Compare(x, y); }
        }
        #endregion

        #region SaveAll/DeleteAll
        //Use default connection (may be overridden in base class)
        public void SaveAll()   {   if (this.Count > 0) { SaveAll(  this[0].DataSrc);    }    }
        public void DeleteAll() {   if (this.Count > 0) { DeleteAll(this[0].DataSrc);    }    }

        //Use connection supplied
        public void SaveAll(  CDataSrc dataSrc) {   dataSrc.BulkSave(this);    }
        public void DeleteAll(CDataSrc dataSrc) {   dataSrc.BulkDelete(this);  }

        //Use transaction supplied
        public void SaveAll(  IDbTransaction txOrNull)    {   foreach (CChapter i in this) {   i.Save(  txOrNull);   }   }
        public void DeleteAll(IDbTransaction txOrNull)    {   foreach (CChapter i in this) {   i.Delete(txOrNull);   }   }

        //Use a specified isolation level
        public void SaveAll(  IsolationLevel txIsolationLevel)   {   if (this.Count > 0) { SaveAll(  this[0].DataSrc, txIsolationLevel);  }    }
        public void DeleteAll(IsolationLevel txIsolationLevel)   {   if (this.Count > 0) { DeleteAll(this[0].DataSrc, txIsolationLevel);  }    }

        //Use a specified connection and isolation level
        public void SaveAll(  CDataSrc dataSrc, IsolationLevel txIsolationLevel)   {   dataSrc.BulkSave(  this, txIsolationLevel);  }
        public void DeleteAll(CDataSrc dataSrc, IsolationLevel txIsolationLevel)   {   dataSrc.BulkDelete(this, txIsolationLevel);  }
        #endregion

        #region List of Ids
        List<int> _ids;
        public List<int> Ids
        {
            get
            {
                if (null == _ids)
                {
                    List<int> ids = new List<int>(this.Count);
                    foreach (CChapter i in this)
                        ids.Add(i.ChapterId);
                    _ids = ids;
                }
                return _ids;
            }
        }
        public CChapterList GetByIds(List<int> ids)
        {
            CChapterList list = new CChapterList(ids.Count);
            foreach (int id in ids)
                if (null != GetById(id))
                    list.Add(GetById(id));
            return list;
        }
        #endregion
        
        #region Cache-Control
        //Main Logic
    
        //Supplementary List Overloads
        public void Add(   IList<CChapter> itemsToAdd)    {   foreach (CChapter i in itemsToAdd)   { Add(   i); }   }
        public void Remove(IList<CChapter> itemsToRemove) {   foreach (CChapter i in itemsToRemove){ Remove(i); }   }
        #endregion
        
        #region Main Index (on ChapterId)
        public CChapter GetById(int chapterId)
        {
            CChapter c = null;
            Index.TryGetValue(chapterId, out c);
            return c;
        }
        [NonSerialized]
        private Dictionary<int,CChapter> _index;
        private Dictionary<int,CChapter> Index
        {
            get
            {
                if (null != _index)
                    if (_index.Count == this.Count)
                        return _index;

                    var temp= new Dictionary<int,CChapter>(this.Count);
				foreach (CChapter i in this)
					temp.Add(i.ChapterId, i);//[i.ChapterId] = i;
				_index = temp;
                return _index;
            }
        }
        #endregion
            
        #region Foreign-Key Indices (Subsets)
        //Index by ChapterSectionId
        public CChapterList GetBySectionId(int sectionId)
        {
            CChapterList temp = null;
            if (! IndexBySectionId.TryGetValue(sectionId, out temp))
            {
                temp = new CChapterList();
                IndexBySectionId[sectionId] = temp;
            }
            return temp;
        }

        [NonSerialized]
        private Dictionary<int, CChapterList> _indexBySectionId;
        private Dictionary<int, CChapterList> IndexBySectionId
        {
            get
            {
                if (null == _indexBySectionId)
                {
                    Dictionary<int, CChapterList> index = new Dictionary<int, CChapterList>();
                    CChapterList temp = null;
                    foreach (CChapter i in this)
                    {
                        if (! index.TryGetValue(i.ChapterSectionId, out temp))
                        {
                            temp = new CChapterList();
                            index[i.ChapterSectionId] = temp;
                        }
                        temp.Add(i);
                    }
                    _indexBySectionId = index;
                }
                return _indexBySectionId;
            }
        }
		//Index by ChapterNodeGuid
		public CChapterList GetByNodeGuids(List<Guid> nodeGuids)
		{
			var temp = new CChapterList();
			foreach (var i in nodeGuids)
				temp.AddRange(GetByNodeGuid(i));
			return temp;
		}

		public CChapterList GetByNodeGuid(Guid nodeGuid)
        {
            CChapterList temp = null;
            if (! IndexByNodeGuid.TryGetValue(nodeGuid, out temp))
            {
                temp = new CChapterList();
                IndexByNodeGuid[nodeGuid] = temp;
            }
            return temp;
        }

        [NonSerialized]
        private Dictionary<Guid, CChapterList> _indexByNodeGuid;
        private Dictionary<Guid, CChapterList> IndexByNodeGuid
        {
            get
            {
                if (null == _indexByNodeGuid)
                {
                    Dictionary<Guid, CChapterList> index = new Dictionary<Guid, CChapterList>();
                    CChapterList temp = null;
                    foreach (CChapter i in this)
                    {
                        if (! index.TryGetValue(i.ChapterNodeGuid, out temp))
                        {
                            temp = new CChapterList();
                            index[i.ChapterNodeGuid] = temp;
                        }
                        temp.Add(i);
                    }
                    _indexByNodeGuid = index;
                }
                return _indexByNodeGuid;
            }
        }
        #endregion

        #region Move Up/Down
        public void MoveUp(  CChapter s) { Move(s, -1); }
        public void MoveDown(CChapter s) { Move(s,  1); }
        public void SortAlphabetically()
        {
            foreach (CChapter i in this)
                i.ChapterSortOrder = 0;
            this.SaveAll();
            CChapter.Cache = null;
        }

        //Private 
        private void Move(CChapter s, int change)
        { 
            if (s == null)
                return;

            int index = this.IndexOf(s) + change;
            if (index < 0) return;
            
            if (index > this.Count - 1)
                return;
            
            //Modify a copy of the array for threadsafety 
            CChapterList dd = new CChapterList(this.Count); 
            dd.AddRange(this);
            dd.Remove(s);
            dd.Insert(index, s);
            dd.ResetOrdinals();
            
            this.SaveAll();
            CChapter.Cache = null;
        }
        private void ResetOrdinals()
        {
            for (int i = 0; i <= this.Count - 1; i++)
                this[i].ChapterSortOrder = i;
        }
		#endregion


		public CChapterList Search(string nameOrId)
		{
			//1. Normalisation
			nameOrId = (nameOrId ?? string.Empty).Trim().ToLower();

			//2. Start with a complete list
			CChapterList results = this;

			//3. Use any available index, such as those generated for fk/bool columns
			//Normal Case - non-unique index (e.g. foreign key)
			//if (int.MinValue != typeId) results = results.GetByTypeId(typeId);
			//if (int.MinValue != parentTagId) results = results.GetByParentTagId(parentTagId);

			//Special case - unique index (e.g. primary key)
			/*
            if (!string.IsNullOrEmpty(nameOrId)) 
            {
                int id;
                if (int.TryParse(nameOrId, out id))
                {
                    CMetaTag obj = this.GetById(id);
                    if (null != obj)
                    {
                        results = new CMetaTagList(1);
                        results.Add(obj);
                        return results;
                    }
                }
            }
            */

			//4. Exit early if remaining (non-index) filters are blank
			if (string.IsNullOrEmpty(nameOrId)) return results;

			//5. Manually search each record using custom match logic, building a shortlist
			CChapterList shortList = new CChapterList();
			foreach (CChapter i in results)
				if (Match(nameOrId, i))
					shortList.Add(i);
			return shortList;
		}
		private bool Match(string name, CChapter obj)
		{
			if (!string.IsNullOrEmpty(name)) //Match any string column
			{
				if (null != obj.ChapterName && obj.ChapterName.ToLower().Contains(name)) return true;
				return false;   //If filter is active, reject any items that dont match
			}
			return true;    //No active filters (should catch this in step #4)
		}
	}
}
